package com.augmentum.login.common;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.augmentum.login.util.StringUtil;

public class BeanFactory {

    private static Map<String, BeanConfig> beans = new HashMap<String, BeanConfig>();
    private static Map<String, Object> objects = new HashMap<String, Object>();
    private static BeanFactory beanFactory;

    private BeanFactory() {

    }

    public Object getBean(String id) {

        if (beans.containsKey(id)) {
            BeanConfig bean = beans.get(id);
            String scope = bean.getScope();
            if (StringUtil.isEmpty(scope)) {
                scope = "singleton";
            }

            if (scope.equalsIgnoreCase("singleton")) {
                if (objects.containsKey(id)) {
                    return objects.get(id);
                }
            }

            String className = bean.getClassName();
            Class<?> clz = null;
            try {
                clz = Class.forName(className);
                Object object = clz.newInstance();
                if (scope.equalsIgnoreCase("singleton")) {
                    objects.put(id, object);
                }
                Method method = null;
                List<BeanProperty> beanPropertys = bean.getBeanPropertys();
                if (beanPropertys != null && !beanPropertys.isEmpty()) {
                    for (BeanProperty beanProperty : beanPropertys) {
                        String beanName = beanProperty.getName();
                        String methodName = beanName.substring(0, 1).toUpperCase() + beanName.substring(1);
                        Method[] methods = clz.getDeclaredMethods();

                        for (Method m : methods) {
                            if (m.getName().equals("set" + methodName)) {
                                method = m;
                                break;
                            }
                        }
                        String ref = beanProperty.getRef();
                        if (!StringUtil.isEmpty(ref.trim())) {
                            method.invoke(object, this.getBean(ref));
                        }
                    }
                }

                if (object.getClass().getPackage().getName().equals("com.augmentum.oes.service.impl")) {
                    ConnectionDynamicProxy connectionDynamicProxy = new ConnectionDynamicProxy();
                    connectionDynamicProxy.setTarget(object);
                    Object proxyObject = Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass()
                            .getInterfaces(), connectionDynamicProxy);
                    return proxyObject;
                }
                return object;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static BeanFactory getInstance() {
        if (beanFactory == null) {
            beanFactory = new BeanFactory();
            beanFactory.init();
        }
        return beanFactory;
    }

    private void init() {

        InputStream inputStream = null;
        try {
            inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.xml");
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(inputStream);
            Element element = document.getDocumentElement();
            NodeList beanNodes = element.getElementsByTagName("bean");
            if (beanNodes == null) {
                return;
            }
            int beanLength = beanNodes.getLength();
            for (int i = 0; i < beanLength; i++) {
                Element beanElement = (Element) beanNodes.item(i);
                BeanConfig bean = new BeanConfig();
                String id = beanElement.getAttribute("id");
                bean.setId(id);

                String className = beanElement.getAttribute("class");
                bean.setClassName(className);

                String scope = beanElement.getAttribute("scope");
                bean.setScope(scope);

                if (beanElement.hasChildNodes()) {
                    NodeList propElems = beanElement.getElementsByTagName("property");
                    if (propElems == null) {
                        return;
                    }
                    for (int j = 0; j < propElems.getLength(); j++) {
                        BeanProperty property = new BeanProperty();
                        Element e = (Element) propElems.item(j);
                        String pname = e.getAttribute("name");
                        String ref = e.getAttribute("ref");
                        String value = e.getAttribute("value");
                        property.setName(pname);
                        property.setRef(ref);
                        property.setValue(value);
                        bean.addProperty(property);
                    }
                }
                beans.put(id, bean);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
